/**
 * Copyright 2009 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

package org.waveprotocol.wave.examples.fedone.agents.maily;
import javax.mail.*;
import javax.mail.internet.*;
import javax.mail.event.*;
import java.util.*; 
import javax.mail.Flags.Flag;
import javax.mail.search.*;
import java.io.IOException;
import java.io.*;
import com.google.common.collect.ImmutableMap;

import org.waveprotocol.wave.examples.fedone.agents.agent.AbstractAgent;
import org.waveprotocol.wave.examples.fedone.agents.agent.AgentConnection;
import org.waveprotocol.wave.examples.fedone.util.Log;
import org.waveprotocol.wave.examples.fedone.waveclient.common.ClientUtils;
import org.waveprotocol.wave.examples.fedone.waveclient.console.ConsoleUtils;
import org.waveprotocol.wave.model.document.operation.AnnotationBoundaryMap;
import org.waveprotocol.wave.model.document.operation.Attributes;
import org.waveprotocol.wave.model.document.operation.AttributesUpdate;
import org.waveprotocol.wave.model.document.operation.BufferedDocOp;
import org.waveprotocol.wave.model.document.operation.DocOpCursor;
import org.waveprotocol.wave.model.document.operation.impl.AttributesImpl;
import org.waveprotocol.wave.model.document.operation.impl.BufferedDocOpImpl.DocOpBuilder;
import org.waveprotocol.wave.model.operation.wave.WaveletDocumentOperation;
import org.waveprotocol.wave.model.wave.ParticipantId;
import org.waveprotocol.wave.model.wave.data.WaveletData;

import java.util.ArrayList;
import java.util.List;

/**
 * Example agent that echoes back operations.
 */
public class Maily extends AbstractAgent {
  private static final Log LOG = Log.get(Maily.class);
  private static final String MAIN_DOCUMENT_ID = "main";
  private static final String name="maily@home-mail.no-ip.fr";
  private Properties props;

  /**
   * Main entry point.
   *
   * @param args program arguments.
   */
  public static void main(String[] args) {
    try {
      if (args.length == 3) {
        int port;
        try {
          port = Integer.parseInt(args[2]);
        } catch (NumberFormatException e) {
          throw new IllegalArgumentException("Must provide valid port.");
        }

        Maily agent = new Maily(args[0], args[1], port);
        agent.run();
      } else {
        System.out.println("usage: java Maily <username> <hostname> <port>");
      }
    } catch (Exception e) {
      LOG.severe("Catastrophic failure", e);
      System.exit(1);
    }

    System.exit(0);
  }

  private Maily(String username, String hostname, int port) {
    super(AgentConnection.newConnection(username, hostname, port));
  }

  private void appendLines(WaveletData wavelet, List<String> lines) {
    BufferedDocOp openDoc = wavelet.getDocuments().get(MAIN_DOCUMENT_ID);
    int docSize = (openDoc == null) ? 0 : ClientUtils.findDocumentSize(openDoc);
    DocOpBuilder builder = new DocOpBuilder();

    if (docSize > 0) {
      builder.retain(docSize);
    }
    builder.elementStart(ConsoleUtils.LINE, new AttributesImpl(
        ImmutableMap.of(ConsoleUtils.LINE_AUTHOR, getParticipantId())));
    builder.elementEnd();
    for (String line : lines) {
      if (!line.isEmpty()) {
        builder.characters(line);
        LOG.info("LINE: " + line);
      }
    }
    WaveletDocumentOperation op =
        new WaveletDocumentOperation(MAIN_DOCUMENT_ID, builder.finish());
    sendWaveletOperation(wavelet.getWaveletName(), op);
  }

  private void appendText(WaveletData wavelet, String text) {
    if (text.isEmpty()) {
      return;
    }
    List<String> lines = new ArrayList<String>(1);
    lines.add(text);
    appendLines(wavelet, lines);
  }

  @Override
  public void onDocumentChanged(WaveletData wavelet, WaveletDocumentOperation documentOperation) {
    LOG.info("onDocumentChanged: " + wavelet.getWaveletName());
    BufferedDocOp docOp = documentOperation.getOperation();
    // Rebuild a similar operation with only the author changed.
    final List<String> lines = new ArrayList<String>();
    docOp.apply(new DocOpCursor() {
        @Override
        public void annotationBoundary(AnnotationBoundaryMap map) {}

        @Override
        public void characters(String chars) {
          lines.add(chars);
        }

        @Override public void deleteCharacters(String chars) {}

        @Override public void deleteElementEnd() {}

        @Override public void deleteElementStart(String type, Attributes attrs) {}
        @Override public void elementEnd() {}
        @Override public void elementStart(String type, Attributes attrs) {
          if (type.equals(ConsoleUtils.LINE)){
            if (attrs.containsKey(ConsoleUtils.LINE_AUTHOR)) {
            }
          }
        }
        @Override public void replaceAttributes(Attributes oldAttrs, Attributes newAttrs) {}
        @Override public void retain(int itemCount) {}
        @Override public void updateAttributes(AttributesUpdate attrUpdate) {}
      });
    //appendLines(wavelet, lines);
    props = new Properties();
    String creator="";
    try{
    	//While waiting from getCreator()
    	creator=wavelet.getParticipants().get(0).getAddress();
	    FileInputStream in = new FileInputStream("src/org/waveprotocol/wave/examples/fedone/agents/maily/profiles/"+creator);
	    props.load(in);
	    in.close();
    }catch (IOException ioe){
    	LOG.info(ioe.getMessage());
    }
    String host = props.getProperty("smtpServer");
    String username = props.getProperty("smtpUsername");
    String password = props.getProperty("smtpPassword"); 
    String body="";
    props.put("mail.smtps.auth", "true");
    String from=creator;
    props.put("mail.from", from);
    Session session = Session.getInstance(props, null);
    try {
    	Transport tp = session.getTransport("smtps");
        MimeMessage msg = new MimeMessage(session);
        msg.setFrom();
        for (ParticipantId p : wavelet.getParticipants()){
        	if(!p.getAddress().equals(this.name))
        		msg.addRecipients(Message.RecipientType.TO,p.getAddress());        
        }
        msg.setSubject("[wave]"+wavelet.getWaveletName());
        msg.setSentDate(new Date());
        body="";
        for (String line : lines) {
        	body+=line;
        }
        msg.setText(body);
        tp.connect(host, username, password);
        tp.sendMessage(msg, msg.getAllRecipients());
        tp.close();
    } catch (MessagingException mex) {
        System.out.println("send failed, exception: " + mex);
    } finally{
    	LOG.info("message correctly sent "+ body+" by "+creator);
    }
  }

  @Override
  public void onParticipantAdded(WaveletData wavelet, ParticipantId participant) {
    LOG.info("onParticipantAdded: " + participant.getAddress());
    appendText(wavelet, participant.getAddress() + " was added to this wavelet.");
  }

  @Override
  public void onParticipantRemoved(WaveletData wavelet, ParticipantId participant) {
    LOG.info("onParticipantRemoved: " + participant.getAddress());
    appendText(wavelet, participant.getAddress() + " was removed from this wavelet.");
  }

  @Override
  public void onSelfAdded(WaveletData wavelet) {
    LOG.info("onSelfAdded: " + wavelet.getWaveletName());
    appendText(wavelet, "I'm listening.");
    this.loop();
  }

  @Override
  public void onSelfRemoved(WaveletData wavelet) {
    LOG.info("onSelfRemoved: " + wavelet.getWaveletName());
    appendText(wavelet, "Goodbye.");
  }			
  
  
  ////////////////////////////////////////////////////////////////////////
  //					MAILBOX MANAGEMENT								//
  ////////////////////////////////////////////////////////////////////////
  private String myLogin;
  private String myPassword;
  private String myProtocole;
  private String myHost;
  private int myPort;
  private Session mySession;
  private Store myStore;
  private Folder myFolder;
  private Message[] myMailBox; 
  
  
  /**
   * Start waiting loop for new emails
   */
  public void loop(){
  	while(true){
  		try{
  			Thread.currentThread().sleep(1000);//sleep for 1000 ms
  		}
  		catch(InterruptedException ie){
  			LOG.info(ie.getMessage());
  		}
		props = new Properties();
		try{
		    FileInputStream in = new FileInputStream("src/org/waveprotocol/wave/examples/fedone/agents/maily/profiles/nicolas@home-mail.no-ip.fr");
		    props.load(in);
		    in.close();
		}catch (IOException ioe){
			LOG.info(ioe.getMessage());
		}
		String host = props.getProperty("imapServer");
		String username = props.getProperty("imapUsername");
		String password = props.getProperty("imapPassword"); 
		int port = Integer.parseInt(props.getProperty("imapPort"));
		this.init(username,password,"imaps",host,port);
		openMailBox("INBOX");
		LOG.info("INBOX: " + nbMessageUnread());
		
		try
		{
			Message[] unread = this.getUnreadMessages();
			for (Message message: unread){
				String subject=message.getSubject();
				if(subject.substring(0,6).equals("[wave]")){
					LOG.info("Next unread subject "+ subject);
					
				}
			}
		}
		catch(MessagingException e)
		{
		   LOG.info(e.getMessage());
		}
  	}
  }
  
  /* ----------------- initialisation de la connection  ---------------*/
  public boolean init(String login, String password, String protocole,
                      String host, int port)
  {
	   
    myLogin = login;
    myPassword = password;
    myProtocole = protocole;
    myHost= host;
    myPort = port;

    Properties props = System.getProperties();

    // création d'une session
    mySession = Session.getDefaultInstance(props,null);
    mySession.setDebug(false);

    //création d'un objet d'enregistrement de message
    myStore = null;

    try
    {
      myStore = mySession.getStore(myProtocole);
      // DEBUG
      System.out.println("store ok !");
    }
    catch (NoSuchProviderException e)
    {
      // DEBUG
      System.out.println("store pas ok ! "+e);
      return false;
    }

    // Connection
    try
    {
      System.out.println("début connection");
      myStore.connect(myHost, myPort, myLogin, myPassword);
      System.out.println("connexion ok !");
    }
    catch ( MessagingException e)
    {
       System.out.println("connexion pas ok ! "+e);
       return false;
    }
    return true;
  }


  /* -----------------------fermeture de la session ---------*/
  /*-----------------------close ----------------------------*/
  public boolean close()
  {

    try
    {
      // fermeture du store
      myFolder.expunge();
      myStore.close();

    }
    catch (MessagingException e)
    {
      return false;
    }

    return true;
  }

  /*--------------------- ouverture d'un répertoire---------------*/
  /* ---------------------- openMailBox --------------------------*/
  public boolean openMailBox(String mailbox)
  {
    // ouverture du répertoire courant INBOX
    myFolder = null;
    try
    {
      myFolder = myStore.getDefaultFolder();

      if(myFolder == null)
      {
        System.out.println("Pas de boite aux lettres par défaut");
        return false;
      }

      // par défaut on récupère les messages dans INBOX
      myFolder = myFolder.getFolder(mailbox);
      if (myFolder == null)
      {
         System.out.println("pas de inbox");
      }

      try
      {
          myFolder.open(Folder.READ_WRITE);
          System.out.println("ficher " + myFolder.getName() + " ouvert");
      }
      catch (MessagingException e)
      {
        System.err.println("[process_folder]: cannot open folder " + myFolder +
                           ": " + e.getMessage());
        e.printStackTrace();
      }

    }
    catch (MessagingException e)
    {

      return false;
    }

    try
    {
      myMailBox = new Message[myFolder.getMessageCount()];
      myMailBox = myFolder.getMessages();
    }
    catch( MessagingException e ){}

    return true;
  }
  
  public Message[] getUnreadMessages() throws MessagingException{
	  try{
		  Message[] unread = myFolder.search(new FlagTerm(new Flags(Flags.Flag.SEEN), false));
		  return unread;
	  } catch( MessagingException e ){
		  LOG.info(e.getMessage());
		  throw e;
	  }
	 
  }

  //retourne le nombre de message contenu dans la boite
  public int nbMessage()
  {
    int totalMessages;
    try
    {
      totalMessages = myFolder.getMessageCount();
    }
    catch( MessagingException e )
    {
      return -1;
    }
     return totalMessages;
  }

  /* ----------------- retourne le nombre de message non lu -----------------*/
  public int nbMessageUnread()
  {
    int nbMessage;
    try
    {
      nbMessage = myFolder.getUnreadMessageCount();
    }
    catch (MessagingException e)
    {
      return -1;
    }
    return nbMessage;
  }

  /* ----------------- retourne le nombre de nouveau message  -----------------*/
  public int nbNewMessage()
  {
    int nbMessage;
    try
    {
      nbMessage = myFolder.getNewMessageCount();
    }
    catch (MessagingException e)
    {
      return -1;
    }
    return nbMessage;
  }

  /*--------------------- retourne le nombre de message non lu ---------------*/
  public int nbMessageRead()
  {
    return (nbMessage()-nbMessageUnread());
  }

  /*--------------------- retourne le nombre d'ancien message  ---------------*/
  public int nbOldMessage()
  {
    return (nbMessage()-nbNewMessage());
  }


  /*------------------------efface le message num ---------------------------*/
  /* ---------------------DeleteMessage--------------------------------------*/
  public boolean deleteMessage(int num)
  {
    try
    {
      myMailBox[num].setFlag(Flags.Flag.DELETED,true);
      return true;
    }
    catch (MessagingException e)
    {
      return false;
    }
  }


  /*------------------------retourner le sujet du message num ---------------------------*/
  /* ---------------------getSubject--------------------------------------*/

  public String getSubject(int num)
  {

    try
    {
      return myMailBox[num].getSubject();
    }
    catch(MessagingException e)
    {
       return null;
    }
  }



  /*-------------- retourner l'adresse du message num --------------------*/
  /* ---------------------GetAdresse--------------------------------------*/

  public String getAdresse(int num)
  {
    Address[] myAdresse;
    InternetAddress myAdr;

    try
    {
      myAdresse = myMailBox[num].getFrom();
      myAdr = new InternetAddress(myAdresse[0].toString());
      return myAdr.getPersonal();
    }
    catch(MessagingException e)
    {
       return null;
    }
  }

  /*------------------ retourner le sujet du message num -----------------*/
  /* ---------------------getSubject--------------------------------------*/

  public String getDate(int num)
  {
    GregorianCalendar myCalendar = new GregorianCalendar();
    String myDate = new String();
    String temp = new String();

    try
    {
      myCalendar.setTime(myMailBox[num].getReceivedDate());

      switch (myCalendar.get(myCalendar.DAY_OF_WEEK))
      {
        case Calendar.SUNDAY:    temp = "dimanche "; break;
        case Calendar.MONDAY:    temp = "lundi "; break;
        case Calendar.TUESDAY:   temp = "mardi "; break;
        case Calendar.WEDNESDAY: temp = "mercredi "; break;
        case Calendar.THURSDAY:  temp = "jeudi "; break;
        case Calendar.FRIDAY:    temp = "vendredi "; break;
        case Calendar.SATURDAY:  temp = "samedi "; break;
     }
    myDate = temp;

    Integer i = new Integer(myCalendar.get(myCalendar.DATE));
    myDate += i.toString();

    switch (myCalendar.get(myCalendar.MONTH))
      {
        case 0:   temp = " janvier"; break;
        case 1:   temp = " février"; break;
        case 2:   temp = " mars"; break;
        case 3:   temp = " avril"; break;
        case 4:   temp = " mai"; break;
        case 5:   temp = " juin"; break;
        case 6:   temp = " juillet"; break;
        case 7:   temp = " août"; break;
        case 8:   temp = " septembre"; break;
        case 9:   temp = " octobre"; break;
        case 10:  temp = " novembre"; break;
        case 11:  temp = " décembre"; break;
      }
    myDate += temp;

    return myDate;
    }
    catch(MessagingException e)
    {
       return null;
    }
  }

  /*------------------ retourner le text du message num -----------------*/
  /* ---------------------getText--------------------------------------*/


  public String getText(Message message)
  {

    Multipart myMulti;
    BodyPart myBody;

    try
    {

      if(message.isMimeType("text/plain"))
      {
    	  message.setFlag(Flags.Flag.SEEN,true);
        return (String)message.getContent();
      }
      else
      {
        myMulti = (Multipart)(message.getContent());
        myBody = myMulti.getBodyPart(0);
        message.setFlag(Flags.Flag.SEEN,true);
        return (String)myBody.getContent();
      }

    }
    catch(MessagingException e)
    {
       return null;
    }

    catch(IOException io)
    {
       return null;
    }
  }


  /*--------retourne si le messages est nouveau ou non-------*/
  /* ---------------------isNew--------------------------------------*/
  public boolean isNew(int num)
  {
    try
    {
      return myMailBox[num].isSet(Flag.RECENT);
    }
    catch(MessagingException e)
    {
       return false;
    }
  }

  /*--------retourne si le messages est lu ou non-------*/
  /* ---------------------isRead--------------------------------------*/
  public boolean isRead(int num)
  {
    try
    {
      return myMailBox[num].isSet(Flag.SEEN);
    }
    catch(MessagingException e)
    {
       return false;
    }
  }
}
